package com.example.compiler;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;

public class MenuCreator {
    public static final String modelPackage = "com.example.aptex.model";
    public static final String routerPackage = "com.example.aptex";

    public static void createMenu(Filer filer, List<MenuContainer> menuContainers) throws IOException {
        final ClassName menuName = ClassName.get(modelPackage, "FMenu");

        MethodSpec fMenuConstructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(int.class, "iconId")
                .addParameter(String.class, "iconName")
                .addParameter(String.class, "functionId")
                .addParameter(Boolean.class, "needLogin")
                .addParameter(Class.class, "cls")
                .addStatement("this.$N = $N", "iconId", "iconId")
                .addStatement("this.$N = $N", "iconName", "iconName")
                .addStatement("this.$N = $N", "functionId", "functionId")
                .addStatement("this.$N = $N", "needLogin", "needLogin")
                .addStatement("this.$N = $N", "cls", "cls")
                .build();

        TypeSpec fMenuType = TypeSpec.classBuilder("FMenu")
                .addModifiers(Modifier.PUBLIC)
                .addField(int.class, "iconId", Modifier.PRIVATE)
                .addField(String.class, "iconName", Modifier.PRIVATE)
                .addField(String.class, "functionId", Modifier.PRIVATE)
                .addField(Boolean.class, "needLogin", Modifier.PRIVATE)
                .addField(Class.class, "cls", Modifier.PRIVATE)
                .addMethod(setterMethodCreator("iconId", int.class))
                .addMethod(setterMethodCreator("iconName", String.class))
                .addMethod(setterMethodCreator("functionId", String.class))
                .addMethod(setterMethodCreator("needLogin", Boolean.class))
                .addMethod(setterMethodCreator("cls", Class.class))
                .addMethod(getterMethodCreator("iconId", int.class))
                .addMethod(getterMethodCreator("iconName", String.class))
                .addMethod(getterMethodCreator("functionId", String.class))
                .addMethod(getterMethodCreator("needLogin", Boolean.class))
                .addMethod(getterMethodCreator("cls", Class.class))
                .addMethod(fMenuConstructor)
                .build();

        JavaFile javaFile = JavaFile.builder(modelPackage, fMenuType)
                .build();
        javaFile.writeTo(filer);

        ClassName mapName = ClassName.get("java.util", "Map");
        ClassName stringName = ClassName.get("java.lang", "String");

        TypeName mapFMenus = ParameterizedTypeName.get(mapName, stringName, menuName);

        final MethodSpec.Builder registerMethodBuilder = MethodSpec.methodBuilder("register");
        registerMethodBuilder.addStatement("$T<$T,$T> menus = new $T<$T,$T>()", Map.class, stringName, menuName, HashMap.class, stringName, menuName);
        menuContainers.forEach(new Consumer<MenuContainer>() {
            @Override
            public void accept(MenuContainer menu) {
                String filedName = "fMenu" + menu.getFunctionId().trim();
                registerMethodBuilder.addStatement("$T $L = new $T($L,$S,$S,$L,$L.class)",
                        menuName, filedName, menuName,
                        menu.getIconId(), menu.getName(), menu.getFunctionId(), menu.getNeedLogin(), menu.getClassName());
                registerMethodBuilder.addStatement("menus.put($S,$L)", menu.getFunctionId(), filedName);
            }
        });

        MethodSpec registerMethod = registerMethodBuilder
                .addModifiers(Modifier.PUBLIC)
                .addModifiers(Modifier.STATIC)
                .addModifiers(Modifier.FINAL)
                .addStatement("return menus")
                .returns(mapFMenus)
                .build();

        TypeSpec routerClass = TypeSpec.classBuilder("Router")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(registerMethod)
                .build();

        javaFile = JavaFile.builder(routerPackage, routerClass)
                .build();
        javaFile.writeTo(filer);
    }

    public static MethodSpec setterMethodCreator(String methodName, Class cls) {
        MethodSpec methodSpec = MethodSpec.methodBuilder("set" + captureName(methodName))
                .addModifiers(Modifier.PUBLIC)
                .addParameter(cls, methodName)
                .addStatement("this.$L = $L", methodName, methodName)
                .build();
        return methodSpec;
    }


    public static MethodSpec getterMethodCreator(String methodName, Class cls) {
        MethodSpec methodSpec = MethodSpec.methodBuilder("get" + captureName(methodName))
                .addModifiers(Modifier.PUBLIC)
                .returns(cls)
                .addStatement("return this.$L", methodName)
                .build();
        return methodSpec;
    }


    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }
}
